#pragma once

#include <memory>
#include <algorithm>
#include <iostream>
#include <functional>

#include "base.h"
#include "exception.h"
#include "platform.h"
#include "program.h"
#include "device.h"
#include "buffer.h"
#include "cmdqueue.h"

using namespace std;

NS_CLPP_BEGIN

class Buffer;
class Kernel;
class Program;
class CommandQueue;
class Context : public ICLObject
{
public:
	static Context * BuildContext(
		cl_platform_id pid,
		cl_device_id did,
		int deviceCount);

	static Context * BuildFirstGPUContext();

public:
	Context();

	Context(cl_context context);

	~Context();

	Context & operator = (cl_context context);

public:
	virtual void AddRef();

	virtual void Release();

	virtual unsigned long RefCount();

public:
	vector<cl_device_id> GetContextDevices();


	Program * CreateProgramWithSource(const char * SourceFile);
	
	void ExcuteKernelByBlock(Kernel * kernel, cl_uint workDimension, size_t globalOffset, size_t globalSize, size_t localSize);


	Buffer * CreateBufferObjectWithData(void * data, size_t size, cl_mem_flags flags);

	void PushDataToBufferByBlock(Buffer * buffer);

	void PullDataFromBufferByBlock(Buffer * buffer);


	cl_context GetRaw();

public:
	typedef std::function<void(const char *, const void *, size_t)> MESSAGE_CALLBACK_TYPE;

	void staticOnContextMessage(const char * errorInfo, const void * clData, size_t size);

	void SetErrorMessageCallback(MESSAGE_CALLBACK_TYPE cbErrorMessage);

private:
	void QueryContextPropData(cl_context_info name, shared_ptr<void> & data, size_t & size);

private:
	cl_context m_ContextObject;

	CLObjectGuard<CommandQueue> m_CommandQueue;

	MESSAGE_CALLBACK_TYPE m_ErrorCallback;
};

NS_CLPP_END

void OnContextMessage(const char * errorInfo, const void * clData, size_t size, void * userData);